#ifndef LARGEDISPLAYMANAGER_H
#define LARGEDISPLAYMANAGER_H

#pragma once


#if defined(__APPLE__)
	#include <ext/hash_map>
	namespace HASH_MAP_EXTENSION = __gnu_cxx;
#elif defined (_WIN32)
	#include <hash_map>
	namespace HASH_MAP_EXTENSION = stdext;
#else
	#include <hash_map>
	namespace HASH_MAP_EXTENSION = std;	
#endif


#include <stack>
#include <vector>
#include <algorithm>
#include <LDF/LargeDisplayFrameworkLayer.h>

class VisComponent;
class FrameTimer;
class LargeDisplayEvent;


/**
 * This class manages all the components in a large display application. It
 * holds the collection of all components and links the input events to them.
 * It is also responsible for managing the composite trees in which the
 * components might be organized.
 *
 * This class should be extended in order to define which objects are created
 * during initialization. In this manner, the specification of an application
 * that makes use of the framework consists in basically extending this class
 * and providing the involved visualization objects together with their
 * behavior strategies.
 *
 * This class also keeps track of the frame rate, of memory usage by the
 * component's properties/buffers, of the assignment of unique IDs to the
 * components, and of multiple-user interaction and their touch indicators.
 *
 * The selection/picking mechanism used is based on a back-buffer rendering of
 * the components using colors calculated from their IDs. However, since it
 * uses graphics package specific code (e.g., OpenGL), it should be
 * implemented in a concrete subclass. The color-ID conversion mechanism (if
 * used) is defined in the VisComponent class.
 *
 * This class is based on the refactoring of the TabletopVis class created by
 * A. Miede and T. Isenberg in the Interactions Lab at the University of
 * Calgary, Canada.
 *
 * @author Fabricio Anastacio - fabriciocfa@yahoo.com
 * @since July 9, 2007
 */
class LARGEDISPLAYFRAMEWORKLAYER_API LargeDisplayManager
{
	// Constants
private:
	/**
	 * The maximum number of components that can be distinguished when using
	 * RGB coding for selection/picking (less than ULONG_MAX, though).
	 */
	const static unsigned long MAX_NUMBER_OF_PICKABLE_OBJECTS =
		(unsigned long) ((UCHAR_MAX + 1) * (UCHAR_MAX + 1) * (UCHAR_MAX + 1) - 1);

public:
	// RESERVED EVENT TYPES
	/** The type for an animation started event. */
	const static unsigned int ANIMATION_STARTED = 1;
	/** The type for an animation over event. */
	const static unsigned int ANIMATION_OVER = 2;
	/** The last reserved event type. */
	const static unsigned int LAST_RESERVED_EVENT_TYPE = 2;


	// Attributes
protected:
	/** The collection of all visualization components. */
	HASH_MAP_EXTENSION::hash_map<unsigned long, VisComponent*> components;
	/** The component that is the root of the composition tree. It doesn't need to be visible. */
	VisComponent* rootComponent;
	/** The number of concurrent users. */
	unsigned int nUsers;
	/** The list of touch indicators that can be shown on the screen. */
	std::vector<VisComponent*> touchIndicators;
	/** The list of the ids of the currently selected component by each user (if zero, there is no selection). */
	std::vector<unsigned long> selectedComponentIds;
	/** List of component ids to have their composition structure (tree) updated. */
	std::vector<unsigned long> componentsToUpdate;
	/** The timer that measures the frame rate to render the components. */
	FrameTimer* frameTimer;
	/** The current value of the FPS rate for rendering the components. */
	double fps;
	/** The width of the display. */
	unsigned int displayWidth;
	/** The height of the display. */
	unsigned int displayHeight;

private:
	/** The identifier to be assigned to the next created component. */
	unsigned long nextComponentId;
	/** The stack of component identifiers that are no longer in use. */
	std::stack<unsigned long> freeIdsStack;


	// Methods
public:
	/**
	 * Default constructor for a LargeDisplayManager object. It takes a pointer
	 * to a timer object in order to keep track of frame rates and animation
	 * timing. If such functionality is not required, timer can be NULL.
	 *
	 * @param timer the frame timer to be used when calculating the fps rate
	 * when rendering the components.
	 */
	LargeDisplayManager(FrameTimer* timer);
	/**
	 * Destructor for a LargeDisplayManager object.
	 */
	virtual ~LargeDisplayManager(void);

	// RENDERING
	/**
	 * Renders all the components attached to the root component and all touch
	 * indicators.
	 *
	 * @param enablePicking if true, draws for picking mode.
	 */
	void renderAll(bool enablePicking = false);
	/**
	 * Renders all the components attached to the root component, except by the
	 * one with the given identifier (no touch indicators are rendered).
	 *
	 * @param exceptionId the id of the component that should not be rendered.
	 * @param enablePicking if true, draws for picking mode.
	 */
	void renderOtherComponents(unsigned long exceptionId, bool enablePicking = false);
	/**
	 * Renders all the components attached to the root component, except by the
	 * ones with the identifier given in the list of exceptions (no touch
	 * indicators are rendered).
	 *
	 * @param exceptionIds the list of ids of the components that should not be
	 * rendered.
	 * @param enablePicking if true, draws for picking mode.
	 */
	void renderOtherComponents(const std::vector<unsigned long>& exceptionIds, bool enablePicking = false);
	/**
	 * Renders all the components attached to the root component that are
	 * containers (i. e., that are not leaves).
	 *
	 * @param enablePicking if true, draws for picking mode.
	 */
	void renderContainers(bool enablePicking = false);
	/**
	 * Renders all the components attached to the root component that are
	 * containers (i. e., that are not leaves), except by the one with the
	 * given identifier.
	 *
	 * @param exceptionId the id of the container that should not be rendered.
	 * @param enablePicking if true, draws for picking mode.
	 */
	void renderOtherContainers(unsigned long exceptionId, bool enablePicking);
	/**
	 * Renders all the touch indicators.
	 */
	void renderAllTouchIndicators();
	/**
	 * Renders only the specified touch indicator.
	 */
	void renderTouchIndicator(unsigned int indicatorIndex);

	// COMPONENT MANAGEMENT
	/**
	 * Adds a component to this manager. Returns true if the component was
	 * successfully added and false if a component with the same id is already
	 * in (in this case, nothing is added). To avoid conflict, it sets the id
	 * of the component being added so that it is unique. If the maximum number
	 * of components is reached, an exception is thrown. Additionally, if the
	 * given component doesn't have a parent, it is made a child of the root
	 * component.
	 *
	 * @param comp the component to be added.
	 * @return true if the component was successfully added.
	 * @throw std::overflow_error when trying to add more elements than the
	 * maximum allowed.
	 */
	bool addComponent(VisComponent* comp);
	/**
	 * Removes a component from this manager. If there is no component with the
	 * given ID, nothing is done. The component is not destroyed, just removed
	 * from the manager (to destroy it, see the deleteComponent() method).
	 * The component is also removed from its parent children list and its
	 * children are made children of its parent node.
	 *
	 * @param id the ID of the component to be deleted.
	 */
	void removeComponent(unsigned long id);
	/**
	 * Deletes a component from this manager. If there is no component with the
	 * given ID, nothing is done. If the component is deleted, its id is freed.
	 * The component is also removed from its parent children list and its
	 * children are made children of its parent node.
	 *
	 * @param id the ID of the component to be deleted.
	 */
	void deleteComponent(unsigned long id);
	/**
	 * Returns the component with the given identifier. If there is no
	 * component with the given identifier in the collection, NULL is returned.
	 *
	 * @param id the id of the component to be returned.
	 * @return a pointer to the component with the specified identifier.
	 */
	VisComponent* getComponent(unsigned long id);
	/**
	 * Checks if the given component is inside or outside a container and
	 * makes the proper updates in the composition tree. The passive buffers of
	 * all components in the component branch are updated if the component is
	 * moved inside the composition tree. Returns true if the component was
	 * moved (i. e., changed its parent component) during the update.
	 *
	 * @param component the component that was moved around.
	 * @return true if the component was moved, false if it remained with the
	 * same parent.
	 */
	bool updateComposition(VisComponent* component);
	/**
	 * Adds the given component id to the list of the components that should be
	 * updated when rendering the next frame. The update moves the component
	 * branch in the composition tree and triggers the corresponding
	 * animations, if necessary.
	 *
	 * @param componentId the id of the component that should be updated.
	 */
	inline void addToUpdateList(unsigned long componentId) { componentsToUpdate.push_back(componentId); };
	/**
	 * Returns the current number of components.
	 *
	 * @return the current number of components.
	 */
	inline unsigned long getNumberComponents() { return (unsigned long) components.size(); };

	// EVENT HANDLING
	/**
	 * Processes the given event. If no target component is specified, the
	 * coordinate values from the event object are used to pick a target
	 * component.
	 *
	 * @param evt the event to be processed.
	 */
	void processEvent(LargeDisplayEvent* evt, VisComponent* target = NULL);
	/**
	 * Sends the given event to the components whose ID is in the given list.
	 *
	 * @param evt the event to be multicast.
	 * @param targetIDs the collection of the IDs of the targets of the multicast.
	 */
	void multicastEvent(LargeDisplayEvent* evt, std::vector<unsigned long> targetIDs);
	/**
	 * Sends the given event to all the current components.
	 *
	 * @param evt the event to be broadcast.
	 */
	void broadcastEvent(LargeDisplayEvent* evt);
	/**
	 * Resizes the display. Here, it only forwards the resizing to the root
	 * component. More specific actions should be taken by the subclasses.
	 *
	 * @param width the new width.
	 * @param hright the new height.
	 */
	virtual void resize(unsigned int width, unsigned int height);

	// MISCELLANEOUS
	/**
	 * Returns the amount of memory in KyloBytes allocated by the active
	 * buffers of all components, that is the amount of memory used by all
	 * i-buffers.
	 *
	 * @return the amount of memory in KyloBytes allocated by the active
	 * buffers of all components.
	 */
	unsigned long getBuffersMemorySizeKBytes();
	/**
	 * Replaces the currently selected component ID for a user by the given ID.
	 * If the ID passed for the currently selected component is not found in
	 * association with a user, nothing is done.
	 *
	 * @param currentID the currently selected ID.
	 * @param newID the ID of the component to be selected.
	 */
	void replaceSelectedID(unsigned long currentID, unsigned long newID);
	/**
	 * Returns the component that is currently at the given coordinates. If
	 * there is no component at the specified position, NULL is returned.
	 *
	 * @param x the x-coordinate of the position of component picking.
	 * @param y the y-coordinate of the position of component picking.
	 * @return the component that is currently at the given coordinates or NULL
	 * if no component is found.
	 */
	inline VisComponent* getComponentAt(unsigned int x, unsigned int y) {
		return getComponent(pickComponent(x, y));
	};
	/**
	 * Unselects the component associated the given user.
	 *
	 * @param userID the ID of the user of the selected component to be
	 * unselected.
	 */
	inline void unselectComponent(unsigned long userID = 0) { selectedComponentIds[userID] = 0; };
	/**
	 * Unselects the component associated all users.
	 */
	inline void unselectAllComponent() {
		for (unsigned int i = 0; i < selectedComponentIds.size(); i++) selectedComponentIds[i] = 0;
	};
	/**
	 * Returns true if the component with the given ID is currently selected by
	 * a user.
	 *
	 * @param componentId the id of the component being checked.
	 * @return true if the component is currently selected by a user.
	 */
	inline bool isComponentSelected(unsigned long componentId) {
		return (std::find(selectedComponentIds.begin(), selectedComponentIds.end(), componentId)
			!= selectedComponentIds.end());
	};
	

	// ACCESSOR METHODS
	/**
	 * Returns the component that is the root of the composition.
	 *
	 * @return the component that is the root of the composition.
	 */
	inline VisComponent* getRootComponent() { return rootComponent; };
	/**
	 * Sets the given component to be the root of the composition.
	 *
	 * @param root the component to be set as the root of the composition.
	 */
	inline void setRootComponent(VisComponent* root) { rootComponent = root; };
	/**
	 * Returns the timer that measures the frame rate for rendering the
	 * components.
	 *
	 * @return the timer that measures the frame rate for rendering the
	 * components.
	 */
	inline FrameTimer* getFrameTimer() { return frameTimer; };
	/**
	 * Sets the timer that measures the frame rate for rendering the
	 * components.
	 *
	 * @param root the frame timer to be set to measure the rendering frame
	 * rates.
	 */
	inline void setFrameTimer(FrameTimer* timer) { frameTimer = timer; };
	/**
	 * Returns the value of the frames per second rate to render the components.
	 *
	 * @return the value of the frames per second rate to render the components.
	 */
	inline double getFPS() { return fps; };
	/**
	 * Returns the current display width.
	 *
	 * @return the current display width.
	 */
	inline unsigned int getDisplayWidth() { return displayWidth; };
	/**
	 * Sets the current display width.
	 *
	 * @param w the value to be set as the current display width.
	 */
	inline void setDisplayWidth(unsigned int w) { displayWidth = w; };
	/**
	 * Returns the current display height.
	 *
	 * @return the current display height.
	 */
	inline unsigned int getDisplayHeight() { return displayHeight; };
	/**
	 * Sets the current display height.
	 *
	 * @param h the value to be set as the current display height.
	 */
	inline void setDisplayHeight(unsigned int h) { displayHeight = h; };
	/**
	 * Returns the list of currently selected component IDs by user.
	 *
	 * @return the list of currently selected component IDs by user.
	 */
	inline std::vector<unsigned long> getSelectedComponentIds() { return selectedComponentIds; };
	/**
	 * Returns the ID of the currently selected component by the given user.
	 *
	 * @param userID the ID of the user of the selected component.
	 * @return the ID of the currently selected component by the user.
	 */
	inline unsigned long getSelectedComponentId(unsigned long userID) { return selectedComponentIds[userID]; };

protected:
	/**
	 * Returns the identifier to be used by the next added component. It may
	 * reuse the identifier of a deleted component, if any is available.
	 *
	 * @return the identifier of the next added component.
	 * @throw std::overflow_error when trying to obtain an id above the maximum
	 */
	unsigned long obtainId();
	/**
	 * Initializes the manager.
	 */
	void initialize();
	/**
	 * Initializes the list of touch indicators.
	 */
	virtual void initTouchIndicators() = 0;
	/**
	 * Initializes the component collection by creating and adding application
	 * specific components.
	 */
	virtual void initComponents() = 0;
	/**
	 * Returns the id of the component at the given coordinates or zero if no
	 * component was picked.
	 *
	 * @param x the x-coordinate of the selection.
	 * @param y the y-coordinate of the selection.
	 * @return the id of the selected component or zero if none was picked.
	 */
	virtual unsigned long pickComponent(unsigned int x, unsigned int y) = 0;
	/**
	 * Returns the id of the container component (i. e., a component that is
	 * not a leaf in the composition tree) at the given coordinates or zero if
	 * no component was picked.
	 *
	 * @param x the x-coordinate of the selection.
	 * @param y the y-coordinate of the selection.
	 * @param excludeComponentId the id of the component that should be
	 * excluded from the picking domain.
	 * @return the id of the selected container component or zero if none was
	 * picked.
	 */
	virtual unsigned long pickContainer(unsigned int x, unsigned int y, unsigned long excludeComponentId) = 0;
};

#endif // LARGEDISPLAYMANAGER_H
